home *** CD-ROM | disk | FTP | other *** search
/ C/C++ Users Group Library 1996 July / C-C++ Users Group Library July 1996.iso / vol_200 / 264_01 / tr.c < prev    next >
Text File  |  1980-01-01  |  6KB  |  263 lines

  1. /*
  2.  * tr - transliterate characters
  3.  *
  4.  * Usage: tr [-bcds] [inset [outset]]
  5.  *
  6.  * Use as a filter. Outset is padded to the length of
  7.  * inset by repeating its last character, if necessary.
  8.  * Inset and outset may contain ranges of the form a-b, where a and/or b
  9.  * may be omitted, and octal numbers of the form \ooo, where ooo is 1-3
  10.  * octal digits.  Combining the two (\1-\5) is allowed.
  11.  * Nulls are acceptable both in the input stream and in the arguments
  12.  * (in the form of an octal escape).
  13.  *
  14.  * Options:
  15.  * -b   operate in binary mode (default is text)
  16.  * -c   complement inset with respect to 1-0377 octal (in ASCII order)
  17.  * -d   delete inset
  18.  * -s   squeeze repeated characters in outset into one on output
  19.  *
  20.  * David MacKenzie
  21.  * Latest revision: 05/19/88
  22.  */
  23.  
  24. #define AZTEC 1            /* Compile for MS-DOS Aztec C? */
  25.  
  26. #define NCHARS 256        /* Size of character set. */
  27.  
  28. #include <stdio.h>
  29.  
  30. #define isoct(d) ((d) >= '0' && (d) <= '7')
  31.  
  32. /* Counted strings, to allow nulls in the arguments (via octal escapes). */
  33. typedef struct {
  34.     int     len;
  35.     char    buf[NCHARS];
  36. } SET;
  37.  
  38. #if AZTEC
  39. #undef getchar            /* To prevent a warning message. */
  40. #define getchar() (bflag ? getc(stdin) : agetc(stdin))
  41. int     bflag = 0;
  42. #endif
  43.  
  44. int     cflag = 0, dflag = 0, sflag = 0;
  45.  
  46. main(argc, argv)
  47.     int     argc;
  48.     char  **argv;
  49. {
  50.     int     optind;
  51.     SET     inset, outset, inset2, *insetp;
  52.  
  53.     for (optind = 1; optind < argc && argv[optind][0] == '-'; ++optind) {
  54.     while (*++argv[optind])
  55.         switch (*argv[optind]) {
  56. #if AZTEC
  57.         case 'b':
  58.         bflag = 1;
  59.         break;
  60. #endif
  61.         case 'c':
  62.         cflag = 1;
  63.         break;
  64.         case 'd':
  65.         dflag = 1;
  66.         break;
  67.         case 's':
  68.         sflag = 1;
  69.         break;
  70.         default:
  71.         usage();
  72.         break;
  73.         }
  74.     }
  75.     if (optind == argc - 2) {
  76.     parse(argv[optind], &inset);
  77.     parse(argv[optind + 1], &outset);
  78.     } else if (optind == argc - 1)
  79.     parse(argv[optind], &inset);
  80.     else if (optind != argc)
  81.     usage();
  82.  
  83.     if (cflag) {
  84.     complement(&inset, &inset2);
  85.     insetp = &inset2;
  86.     } else
  87.     insetp = &inset;
  88.  
  89.     pad(insetp, &outset);
  90.  
  91.     tr(insetp, &outset);
  92.  
  93.     exit(0);
  94. }
  95.  
  96. parse(in, out)
  97.     char   *in;            /* Null-terminated string. */
  98.     SET    *out;
  99. {
  100.     SET     tempset;
  101.  
  102.     simpoct(in, &tempset);
  103.     simprange(&tempset, out);
  104. }
  105.  
  106. /*
  107.  * Copy string in to string out, with octal escapes simplified to their
  108.  * actual value.
  109.  */
  110.  
  111. simpoct(in, out)
  112.     char   *in;            /* Null-terminated string. */
  113.     SET    *out;
  114. {
  115.     int     i;            /* Digit counter for octal escapes. */
  116.     char   *outp;
  117.  
  118.     outp = out->buf;
  119.     while (*in)
  120.     if (*in == '\\') {
  121.         *outp = 0;
  122.         for (i = 0, ++in; i < 3 && isoct(*in); ++i, ++in)
  123.         *outp = *outp * 8 + *in - '0';
  124.         if (i == 0)
  125.         *outp = *in++;    /* \d, where !isoct(d), = d */
  126.         ++outp;
  127.     } else
  128.         *outp++ = *in++;
  129.  
  130.     out->len = outp - out->buf;
  131. }
  132.  
  133. /*
  134.  * Copy string in to string out, with character ranges simplified to the
  135.  * actual range of values.
  136.  */
  137.  
  138. simprange(in, out)
  139.     SET    *in, *out;
  140. {
  141.     char    first, last;    /* First, last chars in range. */
  142.     char   *inp, *outp;
  143.  
  144.     inp = in->buf;
  145.     outp = out->buf;
  146.     while (inp < in->buf + in->len)
  147.     if (*inp == '-') {
  148.         if (outp == out->buf)
  149.         /* "-..." = "\1-..." */
  150.         *outp++ = 1;
  151.         first = outp[-1];
  152.         ++inp;
  153.         if (inp == in->buf + in->len)
  154.         /* "...-" = "...-\377" */
  155.         last = 0377;
  156.         else
  157.         last = *inp++;
  158.         for (++first; first <= last; ++first)
  159.         *outp++ = first;
  160.     } else
  161.         *outp++ = *inp++;
  162.     out->len = outp - out->buf;
  163. }
  164.  
  165. /*
  166.  * Put the complement of in with respect to 1-0377 octal into out,
  167.  * in ASCII order.
  168.  */
  169.  
  170. complement(in, out)
  171.     SET    *in, *out;
  172. {
  173.     char   *outp;
  174.     int     i;
  175.  
  176.     outp = out->buf;
  177.     for (i = 1; i <= 0377; ++i)
  178.     if (indexo(in, i) == -1)
  179.         *outp++ = i;
  180.     out->len = outp - out->buf;
  181. }
  182.  
  183. /*
  184.  * If necessary, pad outset to the length of inset with outset's last
  185.  * character.
  186.  */
  187.  
  188. pad(inset, outset)
  189.     SET    *inset, *outset;
  190. {
  191.     char    last;
  192.  
  193.     last = outset->buf[outset->len - 1];
  194.     while (outset->len < inset->len)
  195.     outset->buf[outset->len++] = last;
  196. }
  197.  
  198. /*
  199.  * Copy standard input to standard output; if a character is a member of
  200.  * inset, transliterate it to the corresponding member of outset.
  201.  */
  202.  
  203. tr(inset, outset)
  204.     SET    *inset, *outset;
  205. {
  206.     int     c;            /* One character of input. */
  207.     int     i;            /* Index into in and out bufs. */
  208.  
  209.     while ((c = getchar()) != EOF)
  210.     if ((i = indexo(inset, c)) != -1) {
  211.         if (!dflag)
  212.         outchar(outset->buf[i], outset);
  213.     } else
  214.         outchar(c, outset);
  215. }
  216.  
  217. /*
  218.  * Send c to standard output, removing duplicate consecutive characters
  219.  * that are members of outset if the -s flag was given.
  220.  */
  221. outchar(c, outset)
  222.     int     c;
  223.     SET    *outset;
  224. {
  225.     static int prevc = -1;
  226.  
  227.     if (!sflag || c != prevc || indexo(outset, c) == -1) {
  228. #if AZTEC
  229.     if (!bflag)
  230.         aputc(c, stdout);
  231.     else
  232. #endif
  233.         putc(c, stdout);
  234.     }
  235.     prevc = c;
  236. }
  237.  
  238. /*
  239.  * Return the offset (0 through s->len - 1) of the first occurrence of
  240.  * character c in s, or -1 if not found. 
  241.  */
  242.  
  243. indexo(s, c)
  244.     SET    *s;
  245.     char    c;
  246. {
  247.     int     i;
  248.  
  249.     for (i = 0; i < s->len && s->buf[i] != c; ++i)
  250.      /* Do nothing. */ ;
  251.     return i < s->len ? i : -1;
  252. }
  253.  
  254. usage()
  255. {
  256. #if AZTEC
  257.     fprintf(stderr, "Usage: tr [-bcds] [inset [outset]]\n");
  258. #else
  259.     fprintf(stderr, "Usage: tr [-cds] [inset [outset]]\n");
  260. #endif
  261.     exit(1);
  262. }
  263.